package cn.kxhp.uploader;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import com.aliyun.sdk.service.cas20200407.AsyncClient;
import com.aliyun.sdk.service.cas20200407.models.*;
import org.apache.commons.cli.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.File;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@SpringBootApplication
public class Application {

    private static UploadUserCertificateResponse uploadUserCertificate(AsyncClient client, String name, String cert, String key) throws Exception {
        UploadUserCertificateRequest request = UploadUserCertificateRequest.builder()
                .name(name)
                .cert(cert)
                .key(key)
                .build();
        CompletableFuture<UploadUserCertificateResponse> future = client.uploadUserCertificate(request);
        return future.get();
    }

    private static ListUserCertificateOrderResponse listUserCertificateOrder(AsyncClient client, String domain) throws Exception {
        ListUserCertificateOrderRequest request = ListUserCertificateOrderRequest.builder()
                .keyword(domain)
                .status("ISSUED")
                .orderType("UPLOAD")
                .build();
        CompletableFuture<ListUserCertificateOrderResponse> future = client.listUserCertificateOrder(request);
        return future.get();
    }

    private static GetUserCertificateDetailResponse getUserCertificateDetail(AsyncClient client, Long certId) throws Exception {
        GetUserCertificateDetailRequest request = GetUserCertificateDetailRequest.builder()
                .certId(certId)
                .certFilter(false)
                .build();
        CompletableFuture<GetUserCertificateDetailResponse> future = client.getUserCertificateDetail(request);
        return future.get();
    }

    private static void updateBucketDomainCert(OSS ossClient, String bucketName, String domainName
            , String previousCert, GetUserCertificateDetailResponseBody certificate) {
        AddBucketCnameRequest request = new AddBucketCnameRequest(bucketName)
                .withDomain(domainName)
                .withCertificateConfiguration(new CertificateConfiguration()
                        // 设置证书公钥。
                        .withPublicKey(certificate.getCert())
                        // 设置证书私钥。
                        .withPrivateKey(certificate.getKey())
                        // 设置当前证书ID。
                        .withPreviousId(previousCert)
                        // 是否强制覆盖证书。
                        .withForceOverwriteCert(false)
                        // 是否删除证书。
                        .withDeleteCertificate(false));

        try {
            ossClient.addBucketCname(request);
        } catch (Exception e) {
            System.err.println("更新证书失败：" + e.getMessage());
        }
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        AsyncClient client = context.getBean(AsyncClient.class);

        Options options = new Options();

        Option option = new Option("a", "action", true, "操作");
        option.setRequired(true);
        options.addOption(option);

        Option option0 = new Option("d", "directory", true, "证书目录");
        options.addOption(option0);

        Option option1 = new Option("n", "name", true, "证书名称");
        options.addOption(option1);

        Option option2 = new Option("k", "key", true, "私钥文件名称");
        options.addOption(option2);

        Option option3 = new Option("c", "cert", true, "证书文件名称");
        options.addOption(option3);

        Option option4 = new Option("deploy", false, "部署证书");
        options.addOption(option4);

        Option option5 = new Option("dn", "domain", true, "域名");
        options.addOption(option5);

        Option option6 = new Option("b", "bucket", true, "存储区域");
        options.addOption(option6);

        Option option7 = new Option("id", true, "证书ID");
        options.addOption(option7);

        CommandLineParser parser = new DefaultParser();
        HelpFormatter helpFormatter = new HelpFormatter();
        CommandLine cmd;
        try {
            cmd = parser.parse(options, args);
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            helpFormatter.printHelp("证书上传工具", options);
            return;
        }
        String action = cmd.getOptionValue('a');
        if ("upload".equals(action)) {
            Long certId = uploadCert(client, cmd);
            if (certId != null && cmd.hasOption("deploy")) {
                OSS ossClient = context.getBean(OSS.class);
                deployCert(ossClient, client, cmd, certId);
            }
        } else if ("deploy".equals(action)) {
            OSS ossClient = context.getBean(OSS.class);
            deployCert(ossClient, client, cmd, null);
        } else {
            System.err.println("不能识别的操作");
        }
    }

    private static Long uploadCert(AsyncClient client, CommandLine cmd) {
        if (!cmd.hasOption('d')) {
            System.out.println("缺少参数 -d 或 --directory");
            return null;
        }
        if (!cmd.hasOption('k')) {
            System.out.println("缺少参数 -k 或 --key");
            return null;
        }
        if (!cmd.hasOption('c')) {
            System.out.println("缺少参数 -c 或 --cert");
            return null;
        }
        if (!cmd.hasOption('n')) {
            System.out.println("缺少参数 -n 或 --name");
            return null;
        }

        String dir = cmd.getOptionValue('d');
        System.out.println("证书目录：" + dir);

        String key = cmd.getOptionValue('k');
        File keyFile = Paths.get(dir, key).toFile();
        System.out.println("私钥文件：" + keyFile.getAbsolutePath());
        if (!keyFile.exists()) {
            System.out.println("私钥文件不存在");
            return null;
        }

        String cert = cmd.getOptionValue('c');
        File certFile = Paths.get(dir, cert).toFile();
        System.out.println("证书文件：" + certFile.getAbsolutePath());
        if (!certFile.exists()) {
            System.out.println("证书文件不存在");
            return null;
        }

        String certText = FileUtil.readUtf8String(certFile);
        String keyText = FileUtil.readUtf8String(keyFile);
        String name = cmd.getOptionValue('n');

        try {
            UploadUserCertificateResponse response = uploadUserCertificate(client, name, certText, keyText);
            Integer statusCode = response.getStatusCode();
            Long certId = response.getBody().getCertId();
            String requestId = response.getBody().getRequestId();
            System.out.println("上传完成 statusCode: " + statusCode + " ,certId: " + certId + ", requestId: " + requestId);
            return certId;
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        return null;
    }

    private static void deployCert(OSS ossClient, AsyncClient asyncClient, CommandLine cmd, Long certId) {
        if (!cmd.hasOption('b')) {
            System.out.println("缺少参数 -b 或 --bucket");
            return;
        }
        if (!cmd.hasOption("domain")) {
            System.out.println("缺少参数 --domain");
            return;
        }
        String bucketName = cmd.getOptionValue('b');
        String domainName = cmd.getOptionValue("domain");
        if (certId == null) {
            String id = cmd.getOptionValue("id");
            if (StrUtil.isNotBlank(id)) {
                try {
                    certId = Long.parseLong(id);
                } catch (NumberFormatException ignore) {
                }
            }
        }
        if (certId == null) {
            System.out.println("缺少参数 --id");
            return;
        }
        System.out.println("查询证书[" + certId + "]详情");
        GetUserCertificateDetailResponseBody certificate = null;
        try {
            GetUserCertificateDetailResponse response = getUserCertificateDetail(asyncClient, certId);
            certificate = response.getBody();
        } catch (Exception e) {
            System.err.println("查询证书失败：" + e.getMessage());
        }
        if (certificate == null) {
            System.out.println("查询证书[" + certId + "]详情失败");
            return;
        }
        CnameConfiguration cname = null;
        List<CnameConfiguration> bucketCname = ossClient.getBucketCname(bucketName);
        for (CnameConfiguration cnameConfiguration : bucketCname) {
            if (cnameConfiguration.getDomain().equals(domainName)) {
                cname = cnameConfiguration;
                break;
            }
        }
        if (cname == null) {
            System.out.println("[" + bucketCname + "]未配置域名[" + domainName + "]");
            return;
        }
        String certRegionId = certificate.getId() + "-cn-hangzhou";
        if (certRegionId.equals(cname.getCertId())) {
            System.out.println("证书已部署，无需更新");
            return;
        }
        System.out.println("开始为域名[" + domainName + "]更新证书");
        updateBucketDomainCert(ossClient, bucketName, domainName, cname.getCertId(), certificate);
        System.out.println("完成域名[" + domainName + "]证书更新");
    }
}
